package com.geek.gkfood.biz.facade.meal.action.update;

import com.geek.gkfood.biz.util.RespUtils;
import com.geek.gkfood.facade.meal.bean.DiscountItem;
import com.geek.gkfood.facade.meal.bean.DishesItem;
import com.geek.gkfood.facade.meal.bean.SnatchOrderReq;
import com.geek.gkfood.facade.meal.bean.SnatchOrderResp;
import com.geek.gkfood.framework.mq.RocketMQSender;
import com.geek.gkfood.framework.redis.RedisOperator;
import com.geek.gkfood.service.meal.model.*;
import com.geek.gkfood.service.redis.SnatchOrderLuaExecutor;
import com.geek.gkfood.util.copy.ShallowCopyUtils;
import com.geek.gkfood.util.excp.GkFoodCodeEnum;
import com.geek.gkfood.util.excp.GkFoodException;
import com.geek.gkfood.util.id.UuidUtils;
import com.geek.gkfood.util.json.JacksonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.MessageChannel;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <h1>餐饮抢单下单处理类。</h1><br/>
 *
 * <h1><br/><a href="https://u.geekbang.org/subject/arch3/100614201">Java 业务架构实战营：https://u.geekbang.org/subject/arch3/100614201</a></h1><br/><h1><a href="https://gitee.com/ylimhhmily/BizArchPracticeCamp">Java 业务架构实战营代码地址：https://gitee.com/ylimhhmily/BizArchPracticeCamp</a></h1><br/><h1><a href="https://u.geekbang.org/subject/java4th/1001148?source=app_share">高级 Java 工程体系课 2023 版：https://u.geekbang.org/subject/java4th/1001148?source=app_share</a></h1><br/><h1><a href="https://time.geekbang.org/column/intro/100312101?tab=catalog">Dubbo 源码剖析与实战：https://time.geekbang.org/column/intro/100312101?tab=catalog</a></h1>
 *
 * @author hmilyylimh
 * ^_^
 * @version 0.0.1
 * ^_^
 * @date 2023/9/26 21:46
 */
@Slf4j
@Component
public class SnatchOrderAction {

    @Autowired
    @Qualifier(Source.OUTPUT)
    private MessageChannel snatchOrderSendChannel;
    @Autowired
    private SnatchOrderLuaExecutor snatchOrderLuaExecutor;

    /**
     * <h2>餐饮抢单下单。</h2>
     *
     * @param: req
     */
    public SnatchOrderResp snatchOrder(SnatchOrderReq req) {
        // 创建抢单模型
        SnatchOrderModel model = createSnatchOrderModel(req);

        // 保存 Redis
        boolean redisSuccFlag = cache2Redis(model);
        // 保存 RocketMQ
        boolean rocketmqSuccFlag = redisSuccFlag ? send2RocketMQ(model) : false;
        // 如果都失败，那就还是当失败处理吧
        throwIfAllFailed(redisSuccFlag, rocketmqSuccFlag);

        // 返回成功响应
        return buildSuccResp(model);
    }

    private boolean cache2Redis(SnatchOrderModel model) {
        SnatchOrderLuaExecutor.LuaModel luaModel = SnatchOrderLuaExecutor.LuaModel.create(
                model.getUserId(), model.getOrderId(), JacksonUtils.toJson(model));

        try {
            return snatchOrderLuaExecutor.tryLock(luaModel);
        } catch (GkFoodException e) {
            log.error("snatchOrderLuaExecutor.tryLock error.", e);
            return true;
        }
    }

    private boolean send2RocketMQ(SnatchOrderModel model) {
        return RocketMQSender.send(snatchOrderSendChannel, JacksonUtils.toJson(model));
    }

    private void throwIfAllFailed(boolean redisSuccFlag, boolean rocketmqSuccFlag) {
        if (!redisSuccFlag && !rocketmqSuccFlag) {
            throw new GkFoodException(GkFoodCodeEnum.SNATCH_ORDER_FAILED);
        }
    }

    private SnatchOrderModel createSnatchOrderModel(SnatchOrderReq req) {
        ShopItemCacheInfo shopItemCacheInfo = RedisOperator.get(req.getShopId(), ShopItemCacheInfo.class);
        Map<String, DishesItemCacheInfo> dishesMap = getDishesItemCacheInfo(req.getShopId(), req.getSelectedDishesList());

        SnatchOrderModel model = new SnatchOrderModel();
        model.setOrderId(UuidUtils.getUuid());

        model.setUserId(req.getCurrUserId());
        model.setUserName(req.getCurrUserName());
        model.setShopId(req.getShopId());

        model.setShopName(shopItemCacheInfo.getShopName());
        model.setSelectedDishesList(buildSelectDishedList(req.getSelectedDishesList(), dishesMap));

        model.setContactPhone(decryptContactPhone(req.getContactPhone()));
        model.setContactName(req.getCurrUserName());

        model.setTotalAmt(calcTotalAmt());
        model.setPayAmt(calcPayAmt(model.getTotalAmt()));
        model.setDiscountDetailList(buildDiscountDetailList());

        model.setBookTime(new Date());

        return model;
    }

    private List<DishesItemModel> buildSelectDishedList(List<DishesItem> selectedDishesList,
                                                        Map<String, DishesItemCacheInfo> dbDishesMap) {
        return null;
    }

    private String decryptContactPhone(String contactPhone) {
        return null;
    }

    private BigDecimal calcTotalAmt() {
        return null;
    }

    private BigDecimal calcPayAmt(BigDecimal totalAmt) {
        return null;
    }

    private List<DiscountItemModel> buildDiscountDetailList() {
        return null;
    }

    private SnatchOrderResp buildSuccResp(SnatchOrderModel model) {
        SnatchOrderResp resp = new SnatchOrderResp();

        resp.setTotalAmt(model.getTotalAmt());
        resp.setPayAmt(model.getPayAmt());
        resp.setDiscountDetailList(ShallowCopyUtils.copyListBeanPropertiesToList(
                model.getDiscountDetailList(), new ArrayList<>(), DiscountItem.class));

        return RespUtils.ok(resp);
    }

    private Map<String, DishesItemCacheInfo> getDishesItemCacheInfo(String shopId, List<DishesItem> selectedDishesList) {
        List<String> dishesIdList = selectedDishesList.stream().map(DishesItem::getDishesId).collect(Collectors.toList());
        return RedisOperator.multiGetMap(shopId, dishesIdList, DishesItemCacheInfo.class);
    }
}